package arg

import (
	"db2s/global"
	"db2s/go-log/log"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gurkankaymak/hocon"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

// 判断库表配置参数是否存在非法参数
func (rc *ConfigParameter) rexPat(rex *regexp.Regexp, rexStr []string) error {
	for _, i := range rexStr {
		if i != "" {
			for _, ii := range strings.Split(i, global.SchemaTableSplit) {
				if !rex.MatchString(ii) {
					return errors.New(fmt.Sprintf("parameter error"))
				}
			}
		}
	}
	return nil
}

func (rc *ConfigParameter) ignoreTableParameter() error {
	var s2 []string
	for k, v := range rc.SecondaryL.SchemaV.IgnoreTableM {
		for _, v1 := range v {
			var tableName string
			tableName = fmt.Sprintf("%s%s%s", k, global.SchemaTableSplit, v1)
			if !rc.SecondaryL.SchemaV.LowerCaseTableNames {
				tableName = fmt.Sprintf("%s%s%s", strings.ToUpper(k), global.SchemaTableSplit, strings.ToUpper(v1))
			}
			s2 = append(s2, tableName)
		}
	}
	var s []SchemaS
	for _, v := range rc.SecondaryL.SchemaV.TableInput {
		v.IgnoreTables = strings.Join(s2, ",")
		s = append(s, v)
	}
	rc.SecondaryL.SchemaV.TableInput = s
	//rc.SecondaryL.SchemaV.IgnoreTables = strings.Join(s2, ",")
	return nil
}
func (rc *ConfigParameter) globalParameter() error {
	var globalForcePart, globalForceIndex, globalWhereAdd, globalWhereSql = make(map[string]string), make(map[string]string), make(map[string]string), make(map[string]string)
	//处理global
	if len(rc.SecondaryL.SchemaV.Global) <= 0 {
		return nil
	}
	for k1 := range rc.SecondaryL.SchemaV.TablesM {
		for k, v := range rc.SecondaryL.SchemaV.Global {
			switch k {
			case "forceIndex":
				if v2, ok := rc.SecondaryL.SchemaV.ForceIndex[k1]; !ok {
					globalForceIndex[k1] = v
				} else {
					globalForceIndex[k1] = v2
				}
			case "forcePart":
				if v2, ok := rc.SecondaryL.SchemaV.ForcePart[k1]; !ok {
					globalForcePart[k1] = v
				} else {
					globalForcePart[k1] = v2
				}
			case "whereAdd":
				if v2, ok := rc.SecondaryL.SchemaV.WhereAdd[k1]; !ok {
					globalWhereAdd[k1] = v
				} else {
					globalWhereAdd[k1] = v2
				}
			case "whereSql":
				if v2, ok := rc.SecondaryL.SchemaV.WhereSql[k1]; !ok {
					globalWhereSql[k1] = v
				} else {
					globalWhereSql[k1] = v2
				}
			}
		}
		_, ok1 := globalWhereSql[k1]
		_, ok2 := globalWhereAdd[k1]
		if ok1 && ok2 {
			vlog := fmt.Sprintf("db2s report: table: %s whereAdd and whereSql parameters cannot be set together! the program will exit!!!", k1)
			fmt.Println(vlog)
			err := errors.New(fmt.Sprintf("whereAdd and whereSql parameters cannot be set together!"))
			return err
		}
	}
	var s []SchemaS
	for _, v := range rc.SecondaryL.SchemaV.TableInput {
		v.WhereSql = globalWhereSql
		v.WhereAdd = globalWhereAdd
		s = append(s, v)
	}
	rc.SecondaryL.SchemaV.TableInput = s
	return nil
}

// 设置的校验表的库表映射处理
func (rc *ConfigParameter) tableNameMapListActive(schemaName, tableName string) (string, string) {
	mapSchema, mapTable := "", ""
	schema, table := "", ""
	if strings.Contains(tableName, ":") { //处理表名映射
		f1 := strings.Split(tableName, ":")
		table = f1[0]
		mapTable = f1[1]
	} else {
		table = tableName
		mapTable = tableName
	}
	if strings.Contains(schemaName, ":") { //处理库名映射
		f1 := strings.Split(schemaName, ":")
		schema = strings.ReplaceAll(f1[0], "@", ".")
		mapSchema = strings.ReplaceAll(f1[1], "@", ".")
	} else {
		schema = strings.ReplaceAll(schemaName, "@", ".")
		mapSchema = strings.ReplaceAll(schemaName, "@", ".")
	}
	schemaTableName := fmt.Sprintf("%s%s%s", schema, global.SchemaTableSplit, table)
	mapSchemaTableName := fmt.Sprintf("%s%s%s", mapSchema, global.SchemaTableSplit, mapTable)
	return schemaTableName, mapSchemaTableName
}
func (rc *ConfigParameter) recursionGetSubOptionsValue(tmpConfig *hocon.Config, subOptionKeyPrefix, AttributesType string, subOptionDefaultValue interface{}) (subOptionResult map[string]interface{}) {
	subOptionResult = make(map[string]interface{})
	switch reflect.TypeOf(subOptionDefaultValue).String() {
	case "string":
		subValue := tmpConfig.GetString(AttributesType)
		if len(subValue) == 0 {
			return
		}
		if len(subOptionKeyPrefix) == 0 {
			if _, ok := subOptionResult[AttributesType]; !ok {
				subOptionResult[AttributesType] = subValue
			}
		} else {
			if _, ok := subOptionResult[subOptionKeyPrefix]; !ok {
				subOptionResult[subOptionKeyPrefix] = map[string]interface{}{AttributesType: subValue}
			}
		}
	case "[]string":
		subValue := tmpConfig.GetStringSlice(AttributesType)
		if len(subValue) == 0 {
			return
		}
		if len(subOptionKeyPrefix) == 0 {
			if _, ok := subOptionResult[AttributesType]; !ok {
				subOptionResult[AttributesType] = subValue
			}
		} else {
			if _, ok := subOptionResult[subOptionKeyPrefix]; !ok {
				subOptionResult[subOptionKeyPrefix] = map[string]interface{}{AttributesType: subValue}
			}
		}
	}
	return
}

type TableObject struct {
	Schema  string `json:"schema"`
	MSchema string `json:"mSchema"`
	Table   string `json:"table"`
	MTable  string `json:"mTable"`
}
type EsaOptions struct {
	Object  TableObject     `json:"object"`
	Options EsaTableOptions `json:"options"`
}

func recursionParameterHoconConfig(ff *hocon.Config, key string) (res any) {
	if ff.Get(key) == nil {
		return
	}
	switch ff.Get(key).Type() {
	case hocon.ObjectType:
		rr := make(map[string]any)
		for k := range ff.GetStringMap(key) {
			pp := ff.GetConfig(key)
			rr[k] = recursionParameterHoconConfig(pp, k)
		}
		return rr
	case hocon.StringType:
		return strings.ReplaceAll(ff.GetString(key), "\"", "")
	case hocon.ArrayType:
		var pp []string
		for _, v := range ff.GetStringSlice(key) {
			pp = append(pp, strings.ReplaceAll(v, "\"", ""))
		}
		return pp
	case hocon.NumberType:
		return ff.GetInt(key)
	case hocon.BooleanType:
		return ff.GetBoolean(key)
	default:
		return
	}
}
func tableSeqSort(newVal any) (seqSort []int, err error) {
	switch newVal.(type) {
	case map[string]any:
		for k := range newVal.(map[string]any) {
			var q int
			if q, err = strconv.Atoi(k); err != nil {
				return
			}
			seqSort = append(seqSort, q)
		}
	}
	sort.Ints(seqSort)
	return
}

func getSchemaNameParameter(s string) (schema, mSchema string) {
	if len(strings.Split(s, ":")) > 1 {
		schema = strings.Split(s, ":")[0]
		mSchema = strings.Split(s, ":")[1]
	} else {
		schema = strings.Split(s, ":")[0]
		mSchema = strings.Split(s, ":")[0]
	}
	return
}
func newTableParameter(ff *hocon.Config) (res []*InputTableAttributes, err error) {
	newVal := recursionParameterHoconConfig(ff, "object")
	var seqSort []int
	if seqSort, err = tableSeqSort(newVal); err != nil {
		return
	}
	for _, seq := range seqSort {
		subVal, ok := newVal.(map[string]any)[strconv.Itoa(seq)]
		if !ok {
			return
		}
		for k, v := range subVal.(map[string]any) {
			var pp = InputTableAttributes{}
			pp.Schema, pp.MSchema = getSchemaNameParameter(k)
			for kk, vv := range v.(map[string]any) {
				pp.Table, pp.MTable = getSchemaNameParameter(kk)
				var q []byte
				if q, err = json.Marshal(vv); err != nil {
					return
				}
				var status int64
				var newPP = pp
				newPP.TableStatus = &status
				newPP.GroupSeq = seq
				var tt TableSubOptions
				if err = json.Unmarshal(q, &tt); err != nil {
					return
				}
				newPP.TableAttributes = tt
				res = append(res, &newPP)
			}
		}
	}
	return
}
func esaTableParameter(ff *hocon.Config) (s []EsaOptions, err error) {
	newVal := recursionParameterHoconConfig(ff, "esa")
	if newVal == nil {
		return
	}
	var esaOptions EsaTableOptions
	for k, v := range newVal.(map[string]any) {
		var ss TableObject
		if len(strings.Split(k, ":")) > 1 {
			ss = TableObject{Schema: strings.Split(k, ":")[0], MSchema: strings.Split(k, ":")[1]}
		} else {
			ss = TableObject{Schema: strings.Split(k, ":")[0], MSchema: strings.Split(k, ":")[0]}
		}
		for kk, vv := range v.(map[string]any) {
			if len(strings.Split(kk, ":")) > 1 {
				ss.Table = strings.Split(kk, ":")[0]
				ss.MTable = strings.Split(kk, ":")[1]
			} else {
				ss.Table = strings.Split(kk, ":")[0]
				ss.MTable = strings.Split(kk, ":")[0]
			}
			var q []byte
			if q, err = json.Marshal(vv); err != nil {
				return
			}
			if err = json.Unmarshal(q, &esaOptions); err != nil {
				return
			}
			s = append(s, EsaOptions{
				Object:  ss,
				Options: esaOptions,
			})
		}
	}
	return
}
func ignoreTableParameter(ff *hocon.Config) (res []TableObject, err error) {
	newVal := recursionParameterHoconConfig(ff, "ignore")
	if newVal == nil {
		return
	}
	for k, v := range newVal.(map[string]any) {
		switch v.(type) {
		case []string:
			for _, vv := range v.([]string) {
				res = append(res, TableObject{
					Schema: k,
					Table:  vv,
				})
			}
		default:
			return
		}
	}
	return
}

type globalParameter struct {
	ForceIndex string `json:"forceIndex"`
	ForcePart  string `json:"forcePart"`
	Force      bool   `json:"force"`
}

func globalTableParameter(ff *hocon.Config) (res globalParameter, err error) {
	newVal := recursionParameterHoconConfig(ff, "global")
	var q []byte
	if q, err = json.Marshal(newVal); err != nil {
		return
	}
	if err = json.Unmarshal(q, &res); err != nil {
		return
	}
	return
}
func schemaIgnoreAction(ff *hocon.Config) (s []*InputTableAttributes, err error) {
	var ignore []TableObject
	var event = "[schemaIgnoreAction]"
	if s, err = newTableParameter(ff); err != nil {
		return
	}
	if ignore, err = ignoreTableParameter(ff); err != nil {
		return
	}
	log.MainLog().Info(event, fmt.Sprintf("arg get %s ignore table", ignore))
	for _, ig := range ignore {
		for _, ss := range s {
			if strings.EqualFold(ig.Schema, ss.Schema) && strings.EqualFold(ig.Table, ss.Table) {
				log.MainLog().Debug(event, fmt.Sprintf(" %v.%v is ignore table", ig.Schema, ig.Table))
				ss.Ignore = true
			}
		}
	}
	return
}
func schemaEsaAction(ff *hocon.Config, s []*InputTableAttributes) (err error) {
	var esa []EsaOptions
	if esa, err = esaTableParameter(ff); err != nil {
		return
	}
	for _, esaTable := range esa {
		for _, table := range s {
			schemaT := strings.EqualFold(esaTable.Object.Schema, table.Schema) && strings.EqualFold(esaTable.Object.MSchema, table.MSchema)
			tableT := strings.EqualFold(esaTable.Object.Table, table.Table) && strings.EqualFold(esaTable.Object.MTable, table.MTable)
			if schemaT && tableT {
				table.TableAttributes.SecColumn.ColumnSlice = esaTable.Options.Columns
				table.TableAttributes.SecColumn.LockFuncName = esaTable.Options.Lock
				table.TableAttributes.SecColumn.UnLockFuncName = esaTable.Options.Unlock
			}
		}
	}
	return
}
func schemaGlobalAction(ff *hocon.Config, s []*InputTableAttributes) (err error) {
	var res globalParameter
	if res, err = globalTableParameter(ff); err != nil {
		return
	}
	for _, ss := range s {
		switch {
		case len(res.ForceIndex) > 0 && len(ss.TableAttributes.ForceIndex) == 0:
			ss.TableAttributes.ForceIndex = res.ForceIndex
		case len(res.ForceIndex) > 0 && len(ss.TableAttributes.ForceIndex) > 0:
			if res.Force {
				ss.TableAttributes.ForceIndex = res.ForceIndex
			}
		}
		switch {
		case len(res.ForcePart) > 0 && len(ss.TableAttributes.ForcePart) == 0:
			ss.TableAttributes.ForcePart = res.ForcePart
		case len(res.ForcePart) > 0 && len(ss.TableAttributes.ForcePart) > 0:
			if res.Force {
				ss.TableAttributes.ForcePart = res.ForcePart
			}
		}
	}
	return
}
func schemaOtherParameter(ff *hocon.Config, s []*InputTableAttributes) (err error) {
	for _, v := range []string{"tableJoin", "object"} {
		newVal := recursionParameterHoconConfig(ff, v)
		switch v {
		case "tableJoin":
			for _, tab := range s {
				tab.TableJoin = fmt.Sprintf("%v", newVal)
			}
		case "object":
			for _, tab := range s {
				tab.IndexObject = fmt.Sprintf("%v", newVal)
			}
		}
	}
	return
}

// schema parameter Check
func schemaSecondaryLevelParameter(ff *hocon.Config) (s []*InputTableAttributes, err error) {
	if s, err = schemaIgnoreAction(ff); err != nil {
		return
	}
	if err = schemaEsaAction(ff, s); err != nil {
		return
	}
	if err = schemaGlobalAction(ff, s); err != nil {
		return
	}
	return
}

// schemaParameterCheck
func schemaParameterCheck(p *SecondaryLevel, s schemaModeCheck) (err error) {
	var (
		tablesS   []string
		schemaMap map[string]any
		tabr      *regexp.Regexp
	)
	//表级别的正则匹配
	if schemaMap, err = global.StructToMapAny(s); err != nil {
		return
	} else {
		if v, ok := schemaMap["funcMode"]; ok {
			var funcExist bool
			for _, v1 := range v.([]string) {
				if strings.EqualFold(p.FunctionalMode, v1) {
					funcExist = true
				}
			}
			if !funcExist {
				return nil
			}
		}
	}
	for k := range p.SchemaV.TablesM {
		tablesS = append(tablesS, k)
	}

	tabr, err = regexp.Compile(`[0-9a-zA-Z!@_{}*%-]`)
	if err != nil {
		return
	}
	if err = rc.rexPat(tabr, tablesS); err != nil {
		return
	}
	if err = rc.rexPat(tabr, strings.Split(p.SchemaV.IgnoreTables, ",")); err != nil {
		//global.Wlog.Error(fmt.Sprintf("(%v) %v table/ignoreTable Variables setting error. error is %v", rc.LogThreadSeq, Event, err))
		return
	}
	if strings.Join(tablesS, ",") == p.SchemaV.IgnoreTables {
		fmt.Println("GreatSQL report: table or ignoreTable Variables setting error, please check the log for details.")
		err = errors.New(fmt.Sprintf("table = ignoreTable"))
		//vlog = fmt.Sprintf("(%v) %v  The test form and the skip form cannot be consistent! error info is %v", rc.LogThreadSeq, Event, err)
		//global.Wlog.Error(vlog)
		return
	}

	//判断*.*之外是否还包含其他的值
	if strings.Contains(strings.Join(tablesS, ","), "*.*") {
		table := strings.Replace(strings.Join(tablesS, ","), "*.*", "", 1)
		for _, i := range strings.Split(table, ",") {
			ii := strings.TrimSpace(i)
			if ii != "" {
				err = errors.New(fmt.Sprintf("The table parameter configures *.* and contains other values!"))
				fmt.Println("GreatSQL report: table Variables setting error, please check the log for details.")
				return
			}
		}
	}

	if strings.Join(tablesS, ",") == "" {
		err = errors.New(fmt.Sprintf("table parameter is empty!"))
		fmt.Println("GreatSQL report: table Variables setting error, please check the log for details.")
		//rc.Err = err
		return
	}
	//vlog = fmt.Sprintf("(%v) %v  check table parameter message is {table: %s ignore table: %s}", rc.LogThreadSeq, Event, rc.SecondaryL.SchemaV.Tables, rc.SecondaryL.SchemaV.IgnoreTables)
	//global.Wlog.Debug(vlog)
	return
}
